<?php

/**
 * This class should manage the tablature/lyrics/meta-info parsing, etc.
 *
 * TODO : Import the tablature stuff from www.greenletters.com
 */

define ("TABFILEv10", "TABFILE V1.0");
define ("TABFILEv11", "TABFILE V1.1");
define ("FILE_PLAIN", "Textfile");
define ("FILE_PHP", "<?");

class SongInfoLogic {

	/* Load up the file */
	private static function fileLoad ($sURLorFilename) {
	  if (is_int(strpos(strtoupper($sURLorFilename), 'HTTP'))) {
	    $sFileToLoad = $sURLorFilename;
	    $bLoadFile=true;
	    print "<!--";
	    $qFile = fOpen($sFileToLoad, "r");
	    print "//-->";
	    if (!$qFile) {
	      $bLoadFile = false;
	    } else {
	      fclose($qFile);
	    }
	  } else {
	    $oURI = apache_lookup_uri($sURLorFilename);
	    $sFileToLoad = $oURI->filename;
	    $bLoadFile = false;

	    if (file_exists($sFileToLoad)) {
	      $bLoadFile = true;
	    }
	  }

	  if ($bLoadFile) {
	    $aLines = file($sFileToLoad);
	  } else {
	    $aLines = false;
	  }

	  return $aLines;
	}

	private static function isValid ($aFileLines) {
	  if ($aFileLines != false) {
	    if (is_int(strpos(strtoupper($aFileLines[0]), TABFILEv10))) {
	      return TABFILEv10;
	    } else {
	      if (is_int(strpos(strtoupper($aFileLines[0]), FILE_PHP))) {
	        return false;
	      } else {
		return FILE_PLAIN;
	      }
	    }
	  } else {
	    return false;
	  }
	}

	private static function getNote ($sNNS, $sKey, $iOffset, $bSharp=false) {
	  $notes =
		array ( array("A", "A"),
			array("A#", "Bb"),
			array("B", "B"),
			array("C", "C"),
			array("C#", "Db"),
			array("D", "D"),
			array("D#", "Eb"),
			array("E", "E"),
			array("F", "F"),
			array("F#", "Gb"),
			array("G", "G"),
			array("G#", "Ab"));
	  $pattern =
		array ( "major" => array(1, 3, 5, 6, 8, 10, 12),
			"minor" => array(1, 3, 4, 6, 8, 9, 11));

	  $nns = array ("I" => 1, "II" => 2, "III" => 3, "IV" => 4,
			"V" => 5, "VI" => 6, "VII" => 7);
	  $nns = array ("III" => 3, "VII" => 7, "VI" => 6, "IV" => 4,
			"V" => 5, "II" => 2, "I" => 1);

	  if (!(strpos($sKey, 'b') === false) || ($sKey == 'F')) {
	     $bSharp = false;
	  } else {
	     $bSharp = true;
	  }

	  if ($bSharp) {
	    $iNoteIndex = 0;
	  } else {
	    $iNoteIndex = 1;
	  }

	  $sOff = "";
	  if ($iOffset == 1) {
	    $sOff = "#";
	  } else if ($iOffset == -1) {
	    $sOff = "b";
	  }

	  if ($sKey == "NNS") {
	    return $sNNS;
	  }

	  //Extract the Key
	  $iLen = strlen($sKey)-1;
	  $scaleType = "major";
	  if ($sKey[$iLen] == "M") {
		$scaleType = "major";
		$sKey = substr($sKey, 0, $iLen);
	  } else if ($sKey[$iLen] == "m") {
		$scaleType = "minor";
		$sKey = substr($sKey, 0, $iLen);
	  }

	  // Find the proper Key
	  foreach ($notes as $key => $aNote) {
	    if ($sKey == $aNote[0] || $sKey == $aNote[1]) {
		$iKey = $key;
	    }
	  }

	  $sNew = $sNNS;
	  foreach ($nns as $roman => $number) {
	    if (!(strpos($sNew, $roman) === false)) {
	       $iTemp = $pattern[$scaleType][$number-1]-1;
	       $sTemp = $notes[($iKey + $iTemp+$iOffset+12) % 12][$iNoteIndex];
	       $sNew = str_replace($roman . $sOff, $sTemp, $sNew);
	    }
	  }

	  return $sNew;
	}

	private static function replaceNNS ($sLine, $sKey, $bSharp) {
	  $anns = array ("VII", "VI", "IV", "V", "III", "II", "I");
	  $aLine = split ('[\t ]+', trim($sLine));

	  $sNew = $sLine;
	  foreach ($aLine as $nns) {
	    $iOff = 0; $sOff = '';
	    if (strpos ($nns, "b") >= 1) { $iOff = -1; $sOff = 'b'; }
	    if (strpos ($nns, "#") >= 1) { $iOff = 1; $sOff = '#'; }

	    $iLength = strlen ($nns);

	    $note = SongInfoLogic::getNote ($nns, $sKey, $iOff, $bSharp);
	    $iPosOfNNS = strpos($sNew, $nns);
	    if (!($iPosOfNNS === false)) {
	      if ($iLength < strlen($note)) {
	         $iLength = strlen($note);
	      } else {
	         for ($i=strlen($note); $i<$iLength; $i++) {
	           $note .= " ";
	         }
	      }
	      $sNew = substr_replace($sNew, $note, $iPosOfNNS, $iLength);
	    }
	  }

	  return " " . $sNew;
	}

	private static function parseTabLine ($sLine, $sKey, $sTabbedKey, $sType) {
	  $notes =
		array ( array("A", "A"),
			array("A#", "Bb"),
			array("B", "B"),
			array("C", "C"),
			array("C#", "Db"),
			array("D", "D"),
			array("D#", "Eb"),
			array("E", "E"),
			array("F", "F"),
			array("F#", "Gb"),
			array("G", "G"),
			array("G#", "Ab"));

	  $sKey = trim($sKey); $sTabbedKey=trim($sTabbedKey);

	  if ($sType == 'tab' || $sType == 'music' || $sType == 'all') {
	    if ($sKey == $sTabbedKey) {
	      return "|" . $sLine;
	    } else {
	      foreach($notes as $iIndex => $aNote) {
	        if ($aNote[0] == $sKey || $aNote[1] == $sKey) {
	          $iKeyIndex = $iIndex;
	        }
	        if ($aNote[0] == $sTabbedKey || $aTabbedNote[1] == $sKey) {
	          $iTabbedKeyIndex = $iIndex;
	        }
	      }
	      $iOffset = (($iKeyIndex + 12) - $iTabbedKeyIndex) % 12;

	      $sTemp = $sLine;

	      for ($iFret = 30; $iFret >= 0; $iFret--) {
		 $iNew = $iFret + $iOffset;
		 while (is_int($iPos = strpos($sTemp, "$iFret"))) {
		   $sTemp = substr_replace ($sTemp, "", $iPos, strlen("$iFret"));
		   $iPos = strpos($sLine, "$iFret");
		   $sLine = substr_replace ($sLine, "$iNew", $iPos, strlen("$iFret"));
		 }
	      }

	      return "|" . $sLine;
	   }

	  } else {
	    return "";
	  }
	}

	private static function parseChordLine ($sLine, $sKey, $sType, $bSharp) {
	  if ($sType == 'chords' || $sType == 'music' || $sType == 'all') {
	    return self::replaceNNS($sLine, $sKey, $bSharp);
	  } else {
	    return "";
	  }
	}

	private static function parseInfoLine ($aInfo, $sLine) {
	  $temp = split(':', $sLine, 2);

	  switch (strtoupper($temp[0])) {
	    case 'TITLE' : $aInfo['title'] = $temp[1]; break;
	    case 'AUTHOR' : $aInfo['author'] = $temp[1]; break;
	    case 'ABSOLUTE' : $aInfo['type'] = 'ABSOLUTE';
	    case 'RELATIVE' : $aInfo['type'] = 'RELATIVE';
	    case 'NOTE' : $aInfo['note'] = $temp[1]; break;
	    case 'LINK' : $aInfo['url'] = $temp[1]; break;
	    case 'TIME' : $aInfo['timesig'] = $temp[1]; break;
	    case 'KEY' : $aInfo['key'] = $temp[1]; break;
	    case 'ARRANGEMENT' : $aInfo['arrangement'] = split(",", $temp[1]); break;
	  }

	  return $aInfo;
	}

	private static function parseLyricLine ($sLine, $sType) {
	  if ($sType == 'lyrics') {
	    return trim($sLine);
	  } else if ( $sType == 'all') {
	    return rtrim($sLine);
	  } else {
	    return "";
	  }
	}

	private static function parseLabelLine ($sLine, $sType) {
	    return trim($sLine);
	}

	private static function parseFile ($aFilelines, $sKey="", $sType="", $bSharps = false) {

	  if (!is_array($aFilelines)) return false;
	  if ($sKey  == "" || $sKey  == null) $sKey  = "NNS";
	  if ($sType == "" || $sType == null) $sType = "all";

	  $sCurrentLabel = "default";
	  $aSections = array($sCurrentLabel => array());
	  $aInfo = array('type' => 'RELATIVE', 'key' => $sKey);
	  foreach ($aFilelines as $sLine) {
	    switch ($sLine[0]) {
		case '$' : $aInfo = self::parseInfoLine($aInfo, substr($sLine, 1)); break;
		case '#' : /* Comment */ break;
		case '@' :
		   $sCurrentLabel = self::parseLabelLine(substr($sLine, 1), $sType);
		   $aSections[$sCurrentLabel] = array();
	           break;
		case '%' :
		   if ($sType == 'chords' || $sType == 'music' || $sType == 'all') {
			array_push($aSections[$sCurrentLabel], self::parseChordLine(substr($sLine, 1), $sKey, $sType, $bSharps));
	           }
		   break;
		case '|' :
		   if ($sType == 'tab' || $sType == 'music' || $sType == 'all') {
	 	        array_push($aSections[$sCurrentLabel], self::parseTabLine(substr($sLine, 1), $sKey, $aInfo['key'], $sType));
		   }
		   break;
		default:
	           if ($sType == 'lyrics' || $sType == 'all') {
			array_push($aSections[$sCurrentLabel], self::parseLyricLine ($sLine, $sType));
	           }
		   break;
	    }
	  }

	  return array($aInfo, $aSections);
	}

	private static function formatHTML ($aInfo, $aSections, $sType = "") {
	  $html = "";

	  $sTitle = $aInfo['title'];
	  $sAuthor = $aInfo['author'];
	  $sTimeSignature = $aInfo['timesig'];
	  $sNote = $aInfo['note'];
	  $aArrangement = $aInfo['arrangement'];
	  $sUrl = $aInfo['url'];

	  if ($sTitle != null && $sTitle != "")
		$html .= "<H1> " . $sTitle . "</H1>";
	  if ($sAuthor != null && $sAuthor != "")
		$html .= "<P>by " . $sAuthor . "</P>";
	  $html .= "<P>";
	  if ($sType != 'lyrics') {
	  if ($sTimeSignature != null && $sTimeSignature != "")
		$html .= "Time Signature : " . $sTimeSignature . "<BR>\n";
	  if ($sUrl != null && $sUrl != "")
		$html .= "External Link : <a href=\"$sUrl\">Click Here</a><BR>\n";
	  if ($sNote != null && $sNote != "")
		$html .= "<I> " . $sNote . "</I><BR>\n";
	  }
	  $html .= "</P>";
	  if ($aArrangement == null || $aArrangement == "")
		$aArrangement = array("default");


	  $html .= "<PRE>\n";

	  if ($aArrangement == "PLAIN")
	  {
		  foreach ($aSections as $aLine)
			$html .= rtrim($aLine) . "\n";
	  }
	  else
	  {
		  foreach ($aArrangement as $sLabel) {
		    $sLabel = trim($sLabel);
		    if ($sLabel != "default") {
		      $html .= "<B><BIG>$sLabel</BIG></B>\n";
		    }
		    foreach ($aSections[$sLabel] as $aLine) {
		      $html .= rtrim($aLine) . "\n";
		    }
		  }
	  }

	  $html .= "</PRE>\n";


	  return $html;
	}


	public static function getSongInformation($string, $key, $type, $bSharps = false)
	{
		$title = "Undisplayable";
		$author = "Filetype";
		$html = "This File is Not Displayable";
		$aLines = false;

		if (strlen($string) < 4) return false;
		if (strlen($string) < 20) $aLines = self::fileLoad($string);
		if ($aLines == false) $aLines = split("\n", $string);

		if (($result = self::isValid($aLines)))
		{
		  if ($result == FILE_PLAIN) {
		    $html =  self::formatHTML (array('title' => "A Song", "author" => "An Author", "arrangement" => "PLAIN"), $aLines);
		    $title = "A Song";
		    $author = "An Author";
		  }

		  if ($result == TABFILEv10) {
		    $tabFile = self::parseFile ($aLines, $key, $type, $bSharp);
		    $title = $tabFile[0]['title'];
			$author = $tabFile[0]['author'];
		    $html = self::formatHTML ($tabFile[0], $tabFile[1], $aArgs['type']);
		  }
		}
		else
		{
			return false;
		}

		return array($title, $author, $html);
	}


}

?>
